var gamejs = require("gamejs");
var matchModule = require("js/match");
var bulletModule = require("js/bullet");

/**
 * Turret : an object representing a turret. It can kill enemies when
 * they are detected and in the turret action radius.
 * 
 * @extends Sprite
 */
 
/**
 * Creates a Turret object in the given position.
 * 
 * @param {Match} match The match where this object will work.
 * @param {[number, number]} coords The turret's position, in [x, y] format.
 * @throws {TypeError} If match isn't a Match object or coords isn't in
 * the correct format.
 * @throws {RangeError} If the given coordinates aren't inside the map
 * recovered from the given Match, or if the given coordinates makes the turret
 * image to be out of the map boundaries.
 * 
 * @constructor
 */
var Turret = exports.Turret = function(match, coords) {
	if( ! (match instanceof matchModule.Match)) {
		throw new TypeError("match isn't a valid Match object");
	}
	
	if( ! match.getMap().isInside(coords)) {
		throw new RangeError("Invalid coordinates");
	}
	
	Turret.superConstructor.apply(this, arguments);
	this.currentMatch = match;
	this.center = coords;
	this.target = null;
	
	this.level = 0;
	this.upgradeList = [
		{price: 100, radius: 100, reloadingTime: 1000, resaleValue: 70 }, // livello 0
		{price: 75, radius: 120, reloadingTime: 800, resaleValue: 100 } // livello 1
	];
	
	this.saleable = true;
	this.type = "turret";
	this.timeFromLastShoot = this.upgradeList[0].reloadingTime;
	this.bullets = new gamejs.sprite.Group();
	this.image = gamejs.image.load(imgPath + "turret.png");
	
	//sets the correct rect
	var dims = this.image.getSize();
	var rectTopLeft = [Math.round(this.center[X] - dims[X] / 2), Math.round(this.center[Y] - dims[Y] / 2)];
	this.rect = new gamejs.Rect(rectTopLeft, dims);
	
	this.obstructiveRadius = Math.round((dims[X] > dims[Y] ? dims[X] : dims[Y]) / 2);
	
	if(( ! match.getMap().isInside(this.rect.topleft)) || ( ! match.getMap().isInside(this.rect.bottomright))) {
		throw new RangeError("Invalid coordinates(image out of boundaries)");
	}
	
	//radius for collideCircle
	this.radius = this.obstructiveRadius;
};

/**
 * Turret extends Sprite.
 */
gamejs.utils.objects.extend(Turret, gamejs.sprite.Sprite);

/**
 * Returns the bounding rectangle of this turret.
 * 
 * @return {gamejs.Rect} The bounding rectangle.
 */
Turret.prototype.getBoundingRect = function() {
	return this.rect;
};

/**
 * Returns the obstructive radius of the turret.
 * 
 * @return {number} The obstructive radius of the turret, in pixel.
 */
Turret.prototype.getObstructiveRadius = function() {
	return this.obstructiveRadius;
};

/**
 * Returns the coordinates of the center of this turret.
 *
 * @returns {[number, number]} The coordinates of the center as vector [x, y].
 */
Turret.prototype.getCenter = function() {
	return this.center;
};

/**
 * Returns the action radius.
 *
 * @return {number} The length of the action radius, in pixel.
 */
Turret.prototype.getActionRadius = function() {
	return (this.upgradeList[this.level]).radius;
};

/**
 * Returns the match where this turret work.
 *
 * @return {Match} The Match object where this turret work.
 */
Turret.prototype.getMatch = function() {
	return this.currentMatch;
};

/**
 * Return the level of this turret.
 *
 * @param {number} The level of this turret.
 */
Turret.prototype.getLevel = function() {
	return this.level;
};

/**
 * Returns the amount of credits needed to build this turret.
 *
 * @return {number} The amount of credits needed to build.
 */
Turret.prototype.priceToBuild = function() {
	return (this.upgradeList[0]).price;
};

/**
 * Returns the amount of credits needed to upgrade this turret.
 *
 * @return {number} The amount of credits needed to upgrade.
 * @throws {Error(InvalidOperationError)} If this turret can't be upgraded.
 */
Turret.prototype.priceToUpgrade = function() {
	if( ! this.canUpgrade()) {
		throw new Error("InvalidOperationError : turret can't be upgraded");
	}
	
	return (this.upgradeList[this.level + 1]).price;
};

/**
 * Returns the resale value of the turret.
 *
 * @return {number} The amount of credits refunded when this turret is sold.
 */
Turret.prototype.getResaleValue = function() {
	return this.upgradeList[this.level].resaleValue;
};

/**
 * Tells if the turret can be upgraded.
 *
 * @return {boolean} True if the turret can be upgraded, false otherwise.
 */
Turret.prototype.canUpgrade = function() {
	return (this.level != (this.upgradeList.length - 1));
};

/**
 * Triggers a turret's upgrade.
 * 
 * @throws {Error(InvalidOperationError)} If this turret can't be upgraded
 * or if player doesn't have enough credits.
 */
Turret.prototype.upgrade = function() {
	if( ! this.canUpgrade()) {
		throw new Error("InvalidOperationError : turret can't be upgraded");
	}
	
	var upgradePrice = this.priceToUpgrade();
	if(this.currentMatch.hasCredit(upgradePrice)) {
		this.currentMatch.changeCredit(-upgradePrice);
		this.level += 1;
	}
	else {
		throw new Error("InvalidOperationError : not enough credits");
	}
}

/**
 * Makes the turret saleable or not.
 *
 * @param {boolean} True to make saleable the turret, false to make
 * it not saleable.
 */
Turret.prototype.setSaleable = function(value) {
	if(value) {
		this.saleable = true;
	}
	else {
		this.saleable = false;
	}
};

/**
 * Tells if the turret is saleable.
 *
 * @return {boolean} True if the turret is saleable, false otherwise.
 */
Turret.prototype.getSaleable = function() {
	return this.saleable;
};

/**
 * Sells the turret, removing it from the map.
 * 
 * @throws {Error(InvalidOperationError)} If the turret isn't present in the map or
 * if it's present but isn't saleable.
 */
Turret.prototype.sell = function() {
	this.currentMatch.getMap().removeTurret(this);
};

/**
 * Updates the turret, allowing it (if it's reloaded) to fire to the enemies
 * in the action radius.
 *
 * @param {number} msDuration The time past from the last call, in ms.
 */
Turret.prototype.update = function(msDuration) {
	//updates the bullets group
	this.bullets.update(msDuration);
	
	this.timeFromLastShoot += msDuration;
	//checks if the turret is ready to shoot again
	if(this.timeFromLastShoot > this.upgradeList[this.level].reloadingTime) {
		var detectedReachableEnemies = [];
		this.currentMatch.getMap().getEnemies().sprites().forEach(function(enemy) {
			if (enemy.isDetected()) {
				//this enemy is detected, checks if is in the action radius
				var enemyCenter = enemy.getCenter();
				var dx = enemyCenter[X] - this.center[X];
				var dy = enemyCenter[Y] - this.center[Y];
				var distance = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
				if(distance <= this.getActionRadius()) {
					detectedReachableEnemies.push(enemy);
				}
			}
		}, this);
		
		//checks if there are enemies both detected and in the action radius
		if(detectedReachableEnemies.length > 0) {
			//selects the enemy to which shoot(if more than one)
			var chosenEnemy = detectedReachableEnemies[0];
			if(detectedReachableEnemies.length > 1) {
				//more than one, uses the x coordinate to decide
				//which to shoot
				for(i = 1; i < detectedReachableEnemies.length; i++) {
					if(detectedReachableEnemies[i].getCenter()[X] < chosenEnemy.getCenter()[X]) {
						chosenEnemy = detectedReachableEnemies[i];
					}
				}
			}
			
			//shoot to the chosen enemy
			this.bullets.add(new bulletModule.Bullet(this.currentMatch, this, chosenEnemy));
			this.timeFromLastShoot = 0;
		}
	}
};


/**
 * Draws the turret in the given Surface.
 *
 * @param {gamejs.Surface} surface The Surface where draw on.
 */
Turret.prototype.draw = function(surface) {
	//draw turret tower
	surface.blit(this.image, this.rect.topleft);
	//draw bullets
	this.bullets.draw(surface);
};